package com.hfzy.ihk.web.freeswitchCall.biz;

import com.alibaba.fastjson.JSONObject;
import com.hfzy.ihk.common.facade.enums.CityType;
import com.hfzy.ihk.common.util.date.DateUtils;
import com.hfzy.ihk.facade.freeswitchCall.vo.CallHistory;
import com.hfzy.ihk.web.freeswitchCall.constant.RedisFields;
import com.hfzy.ihk.web.freeswitchCall.constant.RedisKey;
import com.hfzy.ihk.web.freeswitchCall.context.EventSocketInitBean;
import com.hfzy.ihk.web.freeswitchCall.constant.Constants;
import com.hfzy.ihk.web.freeswitchCall.context.DisruptorInitBean;
import com.hfzy.ihk.web.freeswitchCall.support.disruptor.DisruptorSupport;
import com.hfzy.ihk.web.freeswitchCall.support.freeswitch.event.FreeswitchCallOutEvent;
import com.hfzy.ihk.web.freeswitchCall.support.websocket.WebSocketUtils;
import com.hfzy.ihk.web.freeswitchCall.vo.CallOutParam;
import com.hfzy.ihk.web.freeswitchCall.vo.FreeswitchGateway;
import com.lmax.disruptor.RingBuffer;
import org.freeswitch.esl.client.transport.message.EslMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.Executor;

/**
 * Freeswitch外呼处理业务类
 *
 * @Auther: Administrator
 * @Date: 2018/10/24 14:52
 * @Description:
 */
@Lazy
@Component
public class FreeswitchCallOutBiz extends DisruptorSupport<CallHistory, FreeswitchCallOutEvent> {

    Logger logger = LoggerFactory.getLogger(FreeswitchCallOutBiz.class);
    @Value("${freeswitch.host}")
    String freeswitchHost;
    @Resource
    private DisruptorInitBean disruptorInitBean;
    @Autowired
    private EventSocketInitBean eslInitBean;
    @Autowired
    private WebSocketUtils webSocketUtils;

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    Executor freeswitchCallTaskExecutor;


    @Override
    protected RingBuffer<FreeswitchCallOutEvent> useRingBuffer(CallHistory data) {
        RingBuffer ringBuffer = null;
        String useType = "Freeswitch呼出";
        ringBuffer = DisruptorInitBean.getRingBufferFreeswitchCallOut();
        checkRingBufferUse(ringBuffer, useType);
        return ringBuffer;
    }


    /**
     * 进队列呼出
     *
     * @param data
     * @throws Exception
     */
    public void todoCallOut(CallHistory data) throws Exception {
        putInRingBuffer(data);
    }

    /**
     * 1.呼出选线流程,先判断当前城市是否有号码，如果没有就默认那广州的号码（从广州队列里面取通道），
     * 2.拿到当前城市的队列以后，判断是否有空闲的通道，然后在判断拿出来的通道是否是空闲，如果不是，就重新再哪一个，直到拿到空闲为止，
     * 3.如果一直拿不到，就放告诉客户端，无可用线路，让前端调用E1外呼
     * @return
     */
    public Map<String,String> callOutProcessOpt(CallHistory data){
        Map<String, String> resultMap = new HashMap<>();
        try {
            String ccid = null;     //iccid
            String ip = null;       //本次外呼使用的设备ip
            String sipUser = null;  //本次外呼使用的设备的sip user
            String sn = null;       //本次外呼使用的设备ID
            String swxName = null;  //本次外呼使用的设备通道
            String phoneNum = null; //本次外呼使用的电话号码
            String snAndtrunkName = null;
            logger.info(">>>>>>>>>>>>>>.开始进行外呼选线city:{}",data.getCity());
            String company = data.getCompany()==null?RedisKey.DEFAULT_COMPANY: data.getCompany();
            String city= null;
            if ("-1".equals(data.getCity().toString())||CityType.fromValue(data.getCity().toString())==null) {//没有城市，或者城市错误
                city = RedisKey.DEFAULT_CITY;
            } else {//有城市
                city= CityType.fromValue(data.getCity().toString()).getDesc();
            }

            String queueName = company+":" + city+ RedisKey.SWX_CHANNEL_QUEUE;
            String queueMapName = company+":" +city+ RedisKey.SWX_CHANNEL_MAP;

            if (!redisTemplate.hasKey(queueName)) { //如果没有这个城市的队列就默认取HFZY:广州:... 的
                queueName = RedisKey.DEFAULT_COMPANY+":"+RedisKey.DEFAULT_CITY  + RedisKey.SWX_CHANNEL_QUEUE;
                queueMapName = RedisKey.DEFAULT_COMPANY+":"+RedisKey.DEFAULT_CITY  + RedisKey.SWX_CHANNEL_MAP;
            }
            while (redisTemplate.opsForList().size(queueName) > 0) {
                Object queueValue = redisTemplate.opsForList().leftPop(queueName);

                if (queueValue == null) {//该队列没有值了返回告诉前端，没可用线路

                    data.setNoEnoughChannel(true); //给个切换E1标识
                    try {
                        Map<String, String> map = new HashMap<>();
                        map.put(Constants.WEB_SOCKET_JSON_KEY_MSG_TYPE, Constants.WEB_SOCKET_MSG_TYPE_LINE_BUSY);
                        String nowStr = DateUtils.formatDate(new Date(), DateUtils.DATE_FORMAT_DATETIME_SSS);
                        map.put(Constants.WEB_SOCKET_JSON_KEY_TIME, nowStr);
                        map.put(Constants.WEB_SOCKET_JSON_KEY_SESSION_ID, data.getSessionId());
                        map.put(Constants.WEB_SOCKET_JSON_KEY_DATA_ID, String.valueOf(data.getDataId()));
                        map.put(Constants.WEB_SOCKET_JSON_KEY_PAGE_FROM, String.valueOf(data.getPageFrom()));
                        map.put(Constants.WEB_SOCKET_JSON_KEY_MACHINE_FROM, data.getMachineFrom());
                        logger.info(">>选线结果：该城市该公司没有可用线路,推送给前端，queueName：",queueName);
                        //将状态推到客户端
                        webSocketUtils.pushToClientByToken(data.getMachineFrom(), JSONObject.toJSONString(map));
                    }catch (Exception e){
                        logger.error(">>推送fs线路不足消息异常1",e );
                    }

                    resultMap.put("result", Constants.FAIL);
                    resultMap.put("msg", "外呼失败，当前没有可用线路，已返回转E1");

                    return resultMap;

                }
                snAndtrunkName = queueValue.toString();

                try{
                    //出队以后删除对应的map
                    redisTemplate.opsForHash().delete(queueMapName,snAndtrunkName);
                }catch (Exception e){
                    logger.error(">>fs选线移除空闲线路异常!",e);
                }


                String[] arg = snAndtrunkName.split(":");
                if (arg.length < 2)
                    break;

                sn = arg[0];
                swxName = arg[1];

                //判断当前取出的通道是否是空闲通道
                Map<String, String> trunkInfo = redisTemplate.opsForHash().entries(sn + ":" + swxName);

                if (trunkInfo == null || trunkInfo.size() == 0) //没有通道信息跳到下一个通道
                    continue;

                String channelState = trunkInfo.get(RedisFields.STATE);
                String  equipmentState = trunkInfo.get(RedisFields.EQUIPMENT_STATE); //设备是否正常使用

                if (channelState == null || !"Idle".equals(channelState)||equipmentState==null||!"1".equals(equipmentState)) //当前通道不空闲 或者设备不可用，跳到下一个通道
                    continue;

                ip = trunkInfo.get(RedisFields.IP);
                sipUser = trunkInfo.get(RedisFields.FS_SIP_USER);
                phoneNum = trunkInfo.get(RedisFields.PHONE_NUM);
                ccid = trunkInfo.get(RedisFields.ICCID);
                break;
            }
            if (sipUser == null || ccid == null || swxName == null) { //没有取到可用的通道 ，告诉给前端没有可用线路了，请使用e1
                data.setNoEnoughChannel(true); //给个切换E1标识


                try {
                    Map<String, String> map = new HashMap<>();
                    map.put(Constants.WEB_SOCKET_JSON_KEY_MSG_TYPE, Constants.WEB_SOCKET_MSG_TYPE_LINE_BUSY);
                    String nowStr = DateUtils.formatDate(new Date(), DateUtils.DATE_FORMAT_DATETIME_SSS);
                    map.put(Constants.WEB_SOCKET_JSON_KEY_TIME, nowStr);
                    map.put(Constants.WEB_SOCKET_JSON_KEY_SESSION_ID, data.getSessionId());
                    map.put(Constants.WEB_SOCKET_JSON_KEY_DATA_ID, String.valueOf(data.getDataId()));
                    map.put(Constants.WEB_SOCKET_JSON_KEY_PAGE_FROM, String.valueOf(data.getPageFrom()));
                    map.put(Constants.WEB_SOCKET_JSON_KEY_MACHINE_FROM, data.getMachineFrom());
                    //将状态推到客户端
                    logger.info(">>选线结果：该城市该公司没有可用线路,推送给前端1，queueName：",queueName);
                    webSocketUtils.pushToClientByToken(data.getMachineFrom(), JSONObject.toJSONString(map));

                }catch (Exception e){
                    logger.error(">>推送fs线路不足消息异常",e );
                }

                resultMap.put("result", Constants.FAIL);
                resultMap.put("msg", "外呼失败，当前没有可用线路，已返回转E1");
                return resultMap;
            }
            logger.info(">>fs呼出选线结果：sn：{}，swxName：{}，ip：{}，sipUser：{}，phoneNum：{}，ccid：{}",
                    sn, swxName, ip, sipUser, phoneNum, ccid);

            String suffixSipUser = sipUser.substring((sipUser.length() - 2), sipUser.length());
            String suffixSwx = swxName.substring(3, swxName.length()).length() > 1 ? swxName.substring(3, swxName.length()) : "0" + swxName.substring(3, swxName.length());

            logger.info(">>fs呼出选线结果，前缀：suffixSipUser：{}，suffixSwx：{}",
                    suffixSipUser, suffixSwx);
            String targetCallPhone = "";
            if(data.getNeedAsr()) {
                targetCallPhone  = "200"+suffixSipUser+suffixSwx+data.getTargetCallPhone();
            }else {
                targetCallPhone  = "199"+suffixSipUser+suffixSwx+data.getTargetCallPhone();
            }

            String callSipUser = data.getMainCallPhone();
            if(phoneNum!=null){
                data.setMainCallPhone(phoneNum);
            }
            data.setCcid(ccid);
            data.setSipUser(callSipUser);

            //提交外呼
            commitFsCpt(data.getSessionId(),targetCallPhone,callSipUser,null);


            resultMap.put("result", Constants.SUCCESS);
            resultMap.put("msg", "提交外呼成功");

        }catch (Exception e){
            logger.error("选线外呼异常!",e);
            resultMap.put("result", Constants.FAIL);
            resultMap.put("msg", "外呼失败，请重试");
        }
        return resultMap;

    }

    /**
     * 阿里云sip线路外呼操作方法
     * @param data
     * @return
     */
    public Map<String,String> aliyunSipCallOutOpt(CallHistory data){

        //用于封装返回结果
        Map<String, String> resultMap = new HashMap<>();

        if (!eslInitBean.getClient().canSend()) {

            logger.warn("event socket can't send ! 重新初始化客户端");
            resultMap.put("result", Constants.FAIL);
            resultMap.put("msg", "freeswitchl连接已经断开,外呼失败");
            //重新初始化客户端
            eslInitBean.initClient();
            return resultMap;
        }

        logger.debug("freeswitchlog:开始进行freeswitch外呼");

        //生成拨打所需的uuid
        try {

            String uuid = data.getSessionId();
            String targetCallPhone=null;
            if(data.getNeedAsr()) {
                targetCallPhone = "03" + data.getTargetCallPhone(); //联通
            }else {

                targetCallPhone = "02" + data.getTargetCallPhone();////联通
            }

            String mainCallPhone = data.getMainCallPhone();
            //提交外呼
            commitFsCpt(uuid,targetCallPhone,mainCallPhone,data.getShowNum());


            resultMap.put("result", Constants.SUCCESS);
            resultMap.put("msg", "提交外呼成功");

        } catch (Exception e) {
            logger.error("freeswitchlog外呼异常!",e);
            resultMap.put("result", Constants.FAIL);
            resultMap.put("msg", "外呼失败，请重试");
        }

        return resultMap;
    }

    /**
     * 联通线路外呼操作方法
     * @param data
     * @return
     */
    public Map<String,String> copCallOutOpt(CallHistory data){

        //用于封装返回结果
        Map<String, String> resultMap = new HashMap<>();

        if (!eslInitBean.getClient().canSend()) {

            logger.warn("event socket can't send ! 重新初始化客户端");
            resultMap.put("result", Constants.FAIL);
            resultMap.put("msg", "freeswitchl连接已经断开,外呼失败");
            //重新初始化客户端
            eslInitBean.initClient();
            return resultMap;
        }

        logger.debug("freeswitchlog:开始进行freeswitch外呼");

        //生成拨打所需的uuid
        try {

            String uuid = data.getSessionId();
            String targetCallPhone=null;
            if(data.getNeedAsr()) {
                targetCallPhone = "4" + data.getTargetCallPhone(); //联通
            }else {

                targetCallPhone = "5" + data.getTargetCallPhone();////联通
            }

            String mainCallPhone = data.getMainCallPhone();
            //提交外呼
            commitFsCpt(uuid,targetCallPhone,mainCallPhone,data.getShowNum());


            resultMap.put("result", Constants.SUCCESS);
            resultMap.put("msg", "提交外呼成功");

        } catch (Exception e) {
            logger.error("freeswitchlog外呼异常!",e);
            resultMap.put("result", Constants.FAIL);
            resultMap.put("msg", "外呼失败，请重试");
        }

        return resultMap;
    }


    /**
     * freeswitch 外呼api调用
     *
     * @param callOutVo
     * @return
     */
    public Map<String, String> callOutOpt(CallOutParam callOutVo) {

        //用于封装返回结果
        Map<String, String> resultMap = new HashMap<>();

        if (!eslInitBean.getClient().canSend()) {

            logger.warn("event socket can't send ! 重新初始化客户端");
            resultMap.put("result", Constants.FAIL);
            resultMap.put("msg", "freeswitchl连接已经断开,外呼失败");
            //重新初始化客户端
            eslInitBean.initClient();
            return resultMap;
        }

        logger.debug("freeswitchlog:开始进行freeswitch外呼");

        //生成拨打所需的uuid
        try {

            String uuid = callOutVo.getExtend();
            String targetCallPhone=null;
            if(callOutVo.getNeedAsr()) {
                targetCallPhone = "4" + callOutVo.getTargetCallPhone(); //联通
//                targetCallPhone = "79" + callOutVo.getTargetCallPhone();//gateway
//                targetCallPhone = "29" + callOutVo.getTargetCallPhone();
            }else {
//                targetCallPhone = "69" + callOutVo.getTargetCallPhone();//gateway
                targetCallPhone = "5" + callOutVo.getTargetCallPhone();////联通
            }

//            targetCallPhone = "199"+"99"+"32" + callOutVo.getTargetCallPhone();

            String mainCallPhone = callOutVo.getMainCallPhone();
            //提交外呼
            commitFsCpt(uuid,targetCallPhone,mainCallPhone,null);


            resultMap.put("result", Constants.SUCCESS);
            resultMap.put("msg", "提交外呼成功");

        } catch (Exception e) {
            logger.error("freeswitchlog外呼异常!",e);
            resultMap.put("result", Constants.FAIL);
            resultMap.put("msg", "外呼失败，请重试");
        }

        return resultMap;
    }


    private void commitFsCpt(String uuid,String targetCallPhone,String sipUser,String showNum){

        //方式：
        //选线呼出1直接呼出： bgapi originate user/1002 &bridge(sofia/internal/{通道号xx}13724059427@10.10.37.161:5064)
        //---->选线呼出2进入拨号计划： bgapi originate user/1070 199{网关号xx}{通道号xx}13724059427

        //公网freeswitch
        String  callParamStr = "";
        if(showNum!=null){ //通过插卡的设备呼出

            callParamStr= "{effective_caller_id_number=" + showNum + "," +
                    "effective_caller_id_name=" + sipUser + "," +
                    "origination_uuid=" + uuid + "}user/" + sipUser + " "+targetCallPhone;;
        }else{ //通过运营商的sip线路呼出 或者阿里云sip线路

            callParamStr= "{origination_caller_id_number=" + sipUser + "," +
                    "origination_caller_id_name=" + sipUser + "," +
                    "origination_uuid=" + uuid + "}user/" + sipUser + " "+targetCallPhone;;
        }

        logger.info("freeswitch本次呼出sessionId为: "+uuid+",参数:" + callParamStr);

        final String callParam = callParamStr;

        //使用异步提交外呼请求
        try {
            freeswitchCallTaskExecutor.execute(() -> {

                try {
                    logger.info("freeswitch开始提交呼出请求,sessionId为:" + uuid);

                    long startTime = System.currentTimeMillis();
                    eslInitBean.addCall(uuid);  //将本次通话添加到由这个服务发起的通话map中
//                    eslInitBean.getClient().sendAsyncApiCommand("originate", callParam);
                    eslInitBean.getClient().sendBackgroundApiCommand("originate", callParam);

                    long endTime = System.currentTimeMillis();

                    logger.info("freeswitch提交外呼请求完毕,sessionId:" + uuid + ",耗时" + (endTime - startTime) + "毫秒");
                } catch (Exception e) {
                    logger.error("freeswitchlog:freeswitch外呼的时候出异常了!", e);
                }

            });
        }catch (Exception e){
            logger.error("fs提交外呼线程池爆了!",e);
        }
    }

    /**
     * 中断操作,挂断电话
     *
     * @return
     */
    public String todoInterruptCall(String sessionId) {
        try {
//            EslMessage message = eslInitBean.getClient().sendSyncApiCommand("uuid_kill", " " + sessionId);
            EslMessage message = eslInitBean.getClient().sendApiCommand("uuid_kill", " " + sessionId);

            if (message != null) {
                logger.info("--------->Freeswitch中断操作成功: callId:{}", sessionId);
                return Constants.SUCCESS;
            } else {
                logger.error("--------->Freeswitch中断操作失败: callId:{}", sessionId);
                return Constants.FAIL;
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("--------->Freeswitch中断操作异常: callId:{}", sessionId, e);
            return Constants.FAIL;
        }

    }

    ;

    /**
     * 获取可以注册的用户
     *
     * @return
     */
    public String getUser() {

        if (!eslInitBean.getClient().canSend()) { //假如连接不可以,直接甩异常
            logger.error("freeswitchlog:freeswitch连接不可用");
            throw new RuntimeException("freeswitchlog:freeswitch连接不可用");
        }

        String user = null;

        int i = 0;

        while (i < 15) {

            String randomUser = getRandomUser();

            List<String> bodyLines = eslInitBean.getClient().sendApiCommand("sofia_contact", randomUser + "@" + freeswitchHost).getBodyLines();

            if (bodyLines.contains("error/user_not_registered")) {

                user = randomUser;

                logger.info("freeswitchlog:" + user + "用户无人注册可以使用");

                break;
            }
            logger.info("freeswitchlog:" + randomUser + "用户已经有人注册,重新获取");

            i++;
        }

        if (i == 15) {

            logger.info("freeswitchlog: 随机获取超过15次,不管了,随便给一个:" + user);

            user = getRandomUser();
        }

        return user;
    }


    /**
     * 获取随机用户,并判断有没有注册
     *
     * @return
     */
    public String getRandomUser() {

        Random random = new Random();

        StringBuilder stringBuilder = new StringBuilder("10");

        stringBuilder.append(random.nextInt(8) + 2).append(random.nextInt(10));

        return stringBuilder.toString();
    }



}
